-- Desc: 减少销量，增加可售库存 -用于订单退款还原库存
-- 传入的数据格式,对于redis来说,lua是原子性的,有事务的

-- key
local skuStockKey = "mall4j_seckill:stock:"
local buyKey = "mall4j_seckill:buy:"
local refundLogKey = "mall4j_seckill:order_refund:"
local refundTimeKey = "mall4j_seckill:order_refund_time:"
-- ================ 以下为函数， 不属于主流程 ================
local function containsValue(list, value)
    for i = 1, #list do
        if list[i] == value then
            return false
        end
    end
    return true
end

-- ================ 开始提交订单锁定库存流程 ================
local params = {}
-- 用于批量获取sku库存信息的key集合
local mgetSkuKeys = {}
-- sku键数组
local skuKey = {}
-- refundId列表
local refundIds = {}
-- 步骤一    入参 KEYS skuId_退款数量_库存id_退款id_activityId_时间_操作_max_userId   skuId_退款数量_库存id_退款id_activityId_时间_max_userId...
for index, paramStr in ipairs(KEYS) do
    -- {skuId, 退款数量, 库存id, 退款id, activityId, 时间, 操作, max, userId}
    local param = {}
    for value in string.gmatch(paramStr, "%d+") do
        table.insert(param, value)
    end
    table.insert(params, param)

    -- 避免一个sku同时进行多个操作时，查询和计算的库存数据操作，所以这里要对区域库存和总库存的key去重
    local key = param[5].."_"..param[1]
    -- 数组没有的sku数组再添加到数组，skuKey用于批量查询后的sku数据绑定
    -- 区域库存去重
    if param[3] ~= "0" and containsValue(skuKey, key.."_"..param[3]) then
        -- sku区域库存key格式： key名称:skuId_仓库id
        table.insert(mgetSkuKeys, skuStockKey..key.."_"..param[3])
        table.insert(skuKey, key.."_"..param[3])
    end
    -- 总库存去重
    if containsValue(skuKey, key) then
        -- sku总库存key格式： key名称:skuId
        table.insert(mgetSkuKeys, skuStockKey..key)
        -- 用数字类型区分总库存
        table.insert(skuKey, key)
    end
end

for i = 1,#params do
    local param = params[i]
    if containsValue(refundIds, param[4]) then
        -- 退款操作时间：key格式： key名称:refundId
        table.insert(mgetSkuKeys, refundTimeKey..param[4])
        table.insert(refundIds, param[4])
    end
end


-- 下面通过mget获取所有key的值，call后面是可变长参数，unpack是将数组变成可变长参数
local skuStr = redis.call('mget', unpack(mgetSkuKeys));

-- 拆分mget中获取到的sku库存信息。  skuStocks： map<skukey, sku库存数据>   skukey：区域库存=skuId_仓库id; 总库存=skuId
local skuStocks = {}
for index, key in ipairs(skuKey) do
    -- sku库存列表
    skuStocks[key] = tonumber(skuStr[index])
end

local skuLength = # skuKey
local refundTimeList = {}
for index, key in ipairs(refundIds) do
    -- {可售库存数量, 锁定库存数量, 销量, sku库存状态}
    local value = skuStr[index + skuLength]
    if not value then
        value = false
    end
    -- sku库存列表
    refundTimeList[key] = value
end

-- 初始化
refundIds = {}
local result = ""
local updateNum = 0
--按照订单来还原库存
for i = 1,#params do
    -- skuId_refundNum(退货数量)_库存id
    local param = params[i]

    -- 删除锁定标记 key-退款id_skuId_库存id
    local isDelete = redis.call('del',refundLogKey..param[4].."_"..param[1].."_"..param[3].."_"..param[6])
    -- 删除锁定标记成功才进行还原库存，让方法幂等, 没有删除锁定标记就跳过本次循环
    if isDelete > 0 then
        local last = refundTimeList[param[4]] == param[6]
        -- 可售库存数量_锁定库存数量_销量_sku库存状态
        local key = param[5].."_"..param[1]
        local skuPointStock = tonumber(skuStocks[key.."_"..param[3]])
        local skuStock = tonumber(skuStocks[key])
        local changeStock = tonumber(param[2])
        -- 事务异常回滚或者不是最后一次操作,删除记录即可，不用更改库存
        -- 退款成功，且是最后一次操作
        if param[7] == "1" and last then
            -- 区域库存
            skuStocks[key.."_"..param[3]] = skuPointStock + changeStock
            -- 总库存
            skuStocks[key] = skuStock + changeStock

            -- 修改限购数量
            if param[8] ~= -1 then
                if (redis.call('exists', buyKey..param[5].."_"..param[9]) == "1") then
                    -- 用户已购数量
                    local boughtNum = tonumber(redis.call('get', buyKey..param[5].."_"..param[9]))
                    -- 更新购买数量
                    table.insert(msetSkuKV, buyKey..param[5].."_"..param[9])
                    table.insert(msetSkuKV, boughtNum - changeStock)
                end
            end

            result = result..param[4].."_"..param[1].."_"..param[3].."_"..param[6].."_"..param[5].."_"..skuPointStock..","
            result = result..param[4].."_"..param[1].."_"..param[3].."_"..param[6].."_"..param[5].."_"..skuStock..","
            updateNum = updateNum + 1
        end

        if last and containsValue(refundIds, param[4]) then
            table.insert(refundIds, param[4])
        end
    end
end

for index, refundIdStr in ipairs(refundIds) do
    redis.call('del', refundTimeKey..refundIdStr)
end


-- 用于批量设置sku库存信息的key和value集合
local msetSkuKV = {}
if updateNum > 0 then
    for index, key in ipairs(skuKey) do
        -- 库存信息 {可售库存数量, 锁定库存数量, 销量, sku库存状态}
        local skuStock = skuStocks[key]
        -- mset sku库存key   总库存格式：  key名称:skuId； 区域库存格式：  key名称:skuId_仓库id
        table.insert(msetSkuKV, skuStockKey..key)
        -- mset sku库存value   格式：  可售库存数量_锁定库存数量_销量_sku库存状态
        table.insert(msetSkuKV, skuStock)
    end
    -- 修改回redis
    redis.call('mset',unpack(msetSkuKV))
end


return result
